package com.layer.server;

import java.awt.TrayIcon.MessageType;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.HashSet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.layer.common.ClientThread;
import com.layer.common.CommunicationInterface;
import com.layer.common.Message;
import com.layer.common.MessageInterface;

public class ServerNode implements CommunicationInterface, MessageInterface {
	
	private ServerSocket server_socket;
	private HashMap<SocketAddress, ClientThread> client_list;
	private Logger logger;
	
	public ServerNode(){
		try {
			this.server_socket = new ServerSocket(Integer.valueOf("5432"));
			this.client_list = new HashMap<SocketAddress, ClientThread>();
			this.logger = LoggerFactory.getLogger(ServerNode.class);
		} catch (NumberFormatException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void connect() {
		while (true){
			try {
				Socket client = this.server_socket.accept();
				ClientThread client_thread = new ClientThread(client,this);
				this.client_list.put(client.getRemoteSocketAddress(), client_thread);
				client_thread.start();
				this.sendMessage(new Message(Message.Type.BROADCAST, client.getRemoteSocketAddress(), null, new Message(Message.Type.CONNECT_CLIENT, client.getRemoteSocketAddress(), null, null)));
				this.logger.info("Client connected at {} local address - {} remote address",client.getLocalSocketAddress().toString(),client.getRemoteSocketAddress().toString());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public void disconnect() {
		try {
			for (ClientThread cli : this.client_list.values()) {
				this.disconnectClient(cli);
			}
			this.server_socket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void receiveMessage(Message msg) {
		switch (msg.getType()) {
		case BROADCAST:{
				this.sendMessage(msg);
			}
			break;
			
		case DISCONNECT_CLIENT:{
				SocketAddress addr = (SocketAddress) msg.getSource();
				ClientThread c = this.client_list.get(addr); 
				if (c != null){
					this.client_list.remove(addr);
					this.disconnectClient(c);
				}
			}
			break;
			
		case REQUEST_CLIENTS:{
			SocketAddress addr = (SocketAddress) msg.getSource();
			ClientThread c = this.client_list.get(addr); 
			if (c != null){
				HashSet<SocketAddress> clients = new HashSet<SocketAddress>(client_list.keySet());
				clients.remove(addr);
				this.sendMessage(new Message(Message.Type.SEND_CLIENTS, c.getSocket().getLocalSocketAddress(), addr, new HashSet<SocketAddress>(clients)));
				}
		}
		break;
		default:{
				//ClientThread c = this.client_list.get(msg.getTarget());
				//if (c != null)
					//c.send(msg);
				this.sendMessage(msg);
			}
			break;
		}
		
	}
	
	private void disconnectClient(ClientThread c){
		try {
			c.disconnect();
			c.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void sendMessage(Message msg) {
		switch (msg.getType()) {
		case BROADCAST:{
				for (ClientThread c : this.client_list.values()) {
					if (c.getSocket().getRemoteSocketAddress() != msg.getSource())
						c.send((Message) msg.getContent());
				}
			}
			break;
			
		default:{
				ClientThread c = this.client_list.get(msg.getTarget());
				if (c != null)
					c.send(msg);
			}
			break;
		}
	}

}
